<?php
namespace classlib;
class xmlparser {
	/**
	 * 将xml解析成数组格式
	 * @param string $xml
	 * @param boolean $isnormal
	 * @return array
	 */
    public static function xml2array(&$xml, $isnormal = true) {
        $xml_parser = new SofeeXmlParser();
        $xml_parser->parseString($xml);
        $data		= $xml_parser->getTree();
     	unset($xml_parser);
        return $data;
    }
	
    /**
     * 将数组解析成xml文本格式
     * @param unknown_type $arr
     * @param unknown_type $level
     * @return string 
     */
    public static function array2xml($arr, $level = 1) {
        $str = ($level == 1)? "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<root>\r\n" : '';
        $space = str_repeat("\t", $level);
        foreach($arr as $key => $val) {
        	if (is_numeric($key)) {
            	$key = 'item';
            }
            if(!is_array($val)) {
            	if (is_string($val) && preg_match('/[&<>"\'\?]+/', $val)) {
            		$str .= $space."<$key><![CDATA[".$val.']]>'."</$key>\r\n";
            	} else {
            		$str .= $space."<$key>".$val."</$key>\r\n";
            	}
            } else {
                $str .= $space."<$key>\r\n".self::array2xml($val, $level+1).$space."</$key>\r\n";
            }
        }
        if($level == 1) {
          	$str .= '</root>';
        }
        return $str;
    }
}

class SofeeXmlParser {
    private $parser;
    private $srcenc;
    private $dstenc;
    private $_struct = array();

    function SofeeXmlParser($srcenc = 'UTF-8', $dstenc = 'UTF-8') {
        $this->srcenc  = $srcenc;
        $this->dstenc  = $dstenc;
        $this->parser  = null;
        $this->_struct = array();
    }

    function free() {
        if (isset($this->parser) && is_resource($this->parser)) {
            xml_parser_free($this->parser);
            unset($this->parser);
        }
    }

    function parseFile($file) {
        $data = @file_get_contents($file) or die("xml 文件开发解析失败!");
        $this->parseString($data);
    }

    function parseString($data) {
        if ($this->srcenc === null) {
            $this->parser = @xml_parser_create() or die('Unable to create XML parser resource.');
        }else {
            $this->parser = @xml_parser_create($this->srcenc) or die('Unable to create XML parser resource with '. $this->srcenc .' encoding.');
        }
        if ($this->dstenc !== null) {
            @xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $this->dstenc) or die('Invalid target encoding');
        }
        xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
        xml_parser_set_option($this->parser, XML_OPTION_SKIP_WHITE, 1);
        if (!@xml_parse_into_struct($this->parser, $data, $this->_struct)) {
            printf("XML error: %s at line %d",  
                    xml_error_string(xml_get_error_code($this->parser)),  
                    xml_get_current_line_number($this->parser) 
            );
            $this->free();
            exit;
        }
        $this->_count = count($this->_struct);
        $this->free();
    }

    function getTree() {
        $i = 0;
        $tree = array();
        $tree = $this->addNode( 
            $tree,  
            $this->_struct[$i]['tag'],  
            (isset($this->_struct[$i]['value'])) ? $this->_struct[$i]['value'] : '',  
            (isset($this->_struct[$i]['attributes'])) ? $this->_struct[$i]['attributes'] : '',  
            $this->getChild($i) 
        );
        unset($this->_struct);
        return ($tree);
    }

    function getChild(&$i) {
        $children = array();
        while (++$i < $this->_count) {
            $tagname = $this->_struct[$i]['tag'];
            $value = isset($this->_struct[$i]['value']) ? $this->_struct[$i]['value'] : '';
            $attributes = isset($this->_struct[$i]['attributes']) ? $this->_struct[$i]['attributes'] : '';

            switch ($this->_struct[$i]['type']) {
                case 'open': 
                    $child = $this->getChild($i);
                    $children = $this->addNode($children, $tagname, $value, $attributes, $child);
                    break;
                case 'complete': 
                    $children = $this->addNode($children, $tagname, $value, $attributes);
                    break;
                case 'cdata': 
                    $children['value'] .= $value;
                    break;
                case 'close': 
                    return $children;
                    break;
            }
        }
    }

    function addNode($target, $key, $value = '', $attributes = '', $child = '') {
        if (!isset($target[$key])) {
            if ($child != '') {
                $target[$key] = $child;
            }
            if ($attributes != '') {
                foreach ($attributes as $k => $v) {
                    $target[$key][$k] = $v;
                }
            }
            if ($value != '') {
            	$target[$key]['value'] = $value;
            }
        } else {
            if (!isset($target[$key][0])) {
                $oldvalue = $target[$key];
                $target[$key] = array();
                $target[$key][0] = $oldvalue;
                $index = 1;
            }else {
                $index = count($target[$key]);
            }
            if ($child != '') {
                $target[$key][$index] = $child;
            }
            if ($attributes != '') {
                foreach ($attributes as $k => $v) {
                    $target[$key][$index][$k] = $v;
                }
            }
            if ($value != '') {
            	$target[$key][$index]['value'] = $value;
            }
        }
        return $target;
    }
}